package com.ld.shieldsb.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.ld.shieldsb.common.core.util.StackTraceUtil;
import com.ld.shieldsb.common.core.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 默认的事物管理器，可以管理单个，多个数据源事物，但并不是分布式事物管理器 不能保证事务统一
 * 
 * @ClassName TransactionManager
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年3月30日 上午9:26:51
 * @example TransactionManager.start(); <br>
 *          try { <br>
 *          //DAO操作 <br>
 *          DAO.execSql("insert into sys_user_roles(id,roleId,userId) values(1,1,1) "); <br>
 *          DAO.execSql("insert into sys_user_roles(id,roleId,userId) values(2,1,2) "); <br>
 *          DAO.execSql("insert into sys_user_roles(id,roleId,userId) values(3,1,3) "); <br>
 *          } catch (Exception e) { <br>
 *          e.printStackTrace(); <br>
 *          } finally { <br>
 *          try { <br>
 *          TransactionManager.commit(); //最后提交 <br>
 *          } catch (Exception e) { <br>
 *          e.printStackTrace(); <br>
 *          } <br>
 *          } <br>
 *
 */
@Slf4j
public class TransactionManager {
    static ThreadLocal<Boolean> inTrans = new ThreadLocal<Boolean>() {
        protected Boolean initialValue() {
            return false;
        }
    };
    static ThreadLocal<String> firstInvoke = new ThreadLocal<String>() {
        protected String initialValue() {
            return "";
        }
    };

    static ThreadLocal<Map<DataSource, Connection>> conns = new ThreadLocal<Map<DataSource, Connection>>();
    static ThreadLocal<Map<DataSource, Exception>> exceptions = new ThreadLocal<Map<DataSource, Exception>>();

    /**
     * 获取调用者信息
     * 
     * @Title getInvoker
     * @author 吕凯
     * @date 2019年3月30日 上午10:35:13
     * @param ele
     * @return String
     */
    public static String getInvoker(StackTraceElement ele) {
        if (ele == null) {
            return "";
        }
        return ele.getClassName() + ":" + ele.getMethodName();
    }

    /**
     * 获取最后一个调用者的堆栈信息
     * 
     * @Title getLastTraceEle
     * @author 吕凯
     * @date 2019年3月30日 上午10:35:24
     * @return StackTraceElement
     */
    public static StackTraceElement getLastTraceEle() {
        List<StackTraceElement> traceElements = StackTraceUtil.getTraceEleList();
        if (traceElements == null) {
            return null;
        }
        String thisClassName = TransactionManager.class.getName();
        for (StackTraceElement ste : traceElements) {
            String callName = ste.getClassName();
            if (!callName.equals(thisClassName)) {
                return ste;
            }

        }
        return null;
    }

    public static void start() {
        StackTraceElement traceElement = getLastTraceEle();
        if (log.isDebugEnabled()) {
            log.debug(traceElement + "");
        }
        if (StringUtils.isEmpty(firstInvoke.get())) {
            firstInvoke.set(getInvoker(traceElement));
        }
        inTrans.set(true);
    }

    public static void commit() throws Exception {
        StackTraceElement traceElement = getLastTraceEle();
        if (log.isDebugEnabled()) {
            log.debug(traceElement + "");
        }
        if (!getInvoker(traceElement).equals(firstInvoke.get())) {
            log.warn("事务调用者不一致，可能存在多层嵌套，放弃提交：" + firstInvoke.get() + ">>" + traceElement + " tracle:" + StackTraceUtil.getTrace());
            return;
        }
        Map<DataSource, Connection> connsMap = conns.get();
        Map<DataSource, Exception> exceptionsMap = exceptions.get();
        try {
            if (exceptionsMap == null || exceptionsMap.isEmpty()) { // 无异常
                if (connsMap == null) { // 无连接直接返回
                    return;
                }
                SQLException e = null;
                for (Connection conn : connsMap.values()) {
                    try {
                        conn.commit();
                    } catch (SQLException ex) {
                        e = ex;
                    } finally {
                        try {
                            conn.setAutoCommit(true);
                            conn.close();
                        } catch (SQLException ex) {
                            log.error("数据库连接commit失败 " + conn + " " + ex.getMessage());
                        }

                    }

                }
                if (e != null) {
                    rollback(); // 有异常回滚
                    throw e;
                }
            } else {
                Exception exception = null;
                for (Exception excep : exceptionsMap.values()) {
                    exception = excep;
                }
                log.warn(exceptionsMap + "");
                log.error("事务提交失败，存在异常,进行回滚！", exception == null ? new Exception("未知异常") : exception);
                rollback(false);
                if (exception != null) {
                    throw exception;
                }
            }
        } finally {
            clear();
        }

    }

    public static void rollback() throws SQLException {
        rollback(true);
    }

    private static void rollback(boolean clear) throws SQLException {
        StackTraceElement traceElement = getLastTraceEle();
        if (log.isDebugEnabled()) {
            log.debug(traceElement + "");
        }
        if (!getInvoker(traceElement).equals(firstInvoke.get())) {
            log.warn("事务调用者不一致，可能存在多层嵌套，放弃回滚：" + firstInvoke.get() + ">>" + traceElement + " tracle:" + StackTraceUtil.getTrace());
            return;
        }
        Map<DataSource, Connection> map = conns.get();
        SQLException e = null;
        if (map == null) {
            return;
        }
        try {
            for (Connection conn : map.values()) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    e = ex;
                } finally {
                    try {
                        conn.setAutoCommit(true);
                        conn.close();
                    } catch (SQLException ex) {
                        log.error("", e);
                    }

                }

            }
            if (e != null) {
                throw e;
            }
        } finally {
            if (clear) {
                clear();
            }
        }

    }

    public static void clear() {
        conns.remove();
        exceptions.remove();
        inTrans.remove();
        firstInvoke.remove();
    }

    public static Connection getCurrentThreadConnection(DataSource ds) throws SQLException {
        Map<DataSource, Connection> map = conns.get();
        Connection conn = null;
        if (map == null) {
            map = new HashMap<DataSource, Connection>();
            conn = ds.getConnection();
            // 如果用户还有不需要事物，且每次都提交的操作，这个需求很怪，不管了
            if (inTrans()) { // 开启了事务，则不自动提交
                conn.setAutoCommit(false);
            }
            map.put(ds, conn);
            conns.set(map);
        } else {
            conn = map.get(ds);
            if (conn != null && !conn.isClosed()) { // 并且未关闭
                return conn;
            }
            conn = ds.getConnection();
            if (inTrans()) { // 开启了事务，则不自动提交
                conn.setAutoCommit(false);
            }
            map.put(ds, conn);
        }
        return conn;

    }

    /**
     * 保存异常信息
     * 
     * @Title setException
     * @author 吕凯
     * @date 2019年3月29日 下午5:17:54
     * @param ds
     * @param e
     * @throws SQLException
     *             void
     */
    public static void saveException(DataSource ds, Exception e) {
        Map<DataSource, Exception> map = exceptions.get();
        if (map == null) {
            map = new HashMap<DataSource, Exception>();
            map.put(ds, e);
            exceptions.set(map);
        } else {
            map.put(ds, e);

        }
    }

    /**
     * 移除异常信息
     * 
     * @Title removeException
     * @author 吕凯
     * @date 2019年3月29日 下午5:26:43 void
     */
    public static void removeException() {
        exceptions.remove();
    }

    /**
     * 关闭连接
     * 
     * @Title removeException
     * @author 吕凯
     * @date 2019年3月29日 下午5:26:43 void
     */
    public static void closeConn(DataSource ds) throws SQLException {
        Map<DataSource, Connection> map = conns.get();
        if (map != null) {
            Connection conn = map.get(ds);
            if (conn != null) {
                conn.close();
            }
            map.remove(ds);
        }
        Map<DataSource, Exception> excpMap = exceptions.get();
        if (excpMap != null) {
            excpMap.remove(ds);
        }
    }

    public static boolean inTrans() {
        return inTrans.get();
    }

    public static boolean giveUpTrans() {
        return inTrans.get();
    }
}
